// Author: Bjorn Schobben

#include "Minimap.h"
#include <math.h>

Minimap::Minimap(const World& world, int renderWidth, int renderHeight, const Camera& camera, const Pixel& mapColor, const Pixel& wallColor, const Pixel& eyeColor): map(0)
{
	Vector eyepoint, direction, plane;

	eyepoint = camera.GetEyepoint();
	direction = camera.GetDirection();
	plane = camera.GetCameraPlane();

	int scaledHeight = renderHeight/6;
	int scaledWidth = renderWidth/6;
	int size = min(scaledHeight, scaledWidth);

	/*

	  Sketch of minimap:


	   2        2
      <->      <->
	  o----------o 
	  | o------o |  2
	  | |      | | 
	  | |      | |
	  | |      | |
	  | o------o |  2
	  o----------o

	*/

	width = size;
	height = size;
	borderlessWidth = width - BORDERWIDTH*2;
	borderlessHeight = width - BORDERWIDTH*2;

	Allocate();

	SetMapRange(eyepoint, world);
	SetEye(eyepoint);
	SetMapPixels(world, mapColor, wallColor);
	SetEyePixels(eyeColor);
	SetFieldOfView(direction, plane);
}

Minimap::Minimap(const Minimap& rhs): map(0), width(rhs.width), height(rhs.height), borderlessWidth(rhs.borderlessWidth), borderlessHeight(rhs.borderlessHeight), fov(rhs.fov), eye(rhs.eye), leftEdge(rhs.leftEdge), rightEdge(rhs.rightEdge), topEdge(rhs.topEdge), bottomEdge(rhs.bottomEdge)
{
	Allocate();

	for(int i=0; i<width*height; i++)
	{
		map[i] = rhs.map[i];
	}
}

Minimap::~Minimap()
{
	if (map)
	{
		delete[] map;
		map = 0;
	}
}

void Minimap::Allocate()
{
	map = new Pixel[width * height];
}


unsigned int Minimap::GetHeight() const
{
	return height;
}

unsigned int Minimap::GetWidth() const
{
	return width;
}

const Pixel& Minimap::GetPixel(unsigned int x, unsigned int y) const
{
	return map[x + width * y];
}

void Minimap::SetPixel(unsigned int x, unsigned int y, const Pixel& pixel)
{
	map[x + width * y] = pixel;
}


void Minimap::SetEye(const Vector& eyepoint)
{
	// Convert eye world coordinates to minimap coordinates (and add the border size)
	eye.SetX( (eyepoint.GetX() - leftEdge) * borderlessWidth / (rightEdge - leftEdge) + BORDERWIDTH);
	eye.SetY( (eyepoint.GetY() - topEdge) * borderlessHeight / (bottomEdge - topEdge) + BORDERHEIGHT); 
}

void Minimap::SetEyePixels(const Pixel& eyeColor)
{
	int x = eye.GetIntX();
	int y = eye.GetIntY();

	// Draw a square around the eye coordinate
	for(int dy=-1; dy<=1; dy++)
	{
		for(int dx=-1; dx<=1; dx++)
		{
			SetPixel(dx+x, dy+y, eyeColor);
		}
	}
}

void Minimap::SetFieldOfView(const Vector& direction, const Vector& plane)
{
	// Setup field of view triangle
	Vector p1(eye.GetX(), eye.GetY());
	Vector p2(eye.GetX() + (direction.GetX() - plane.GetX())*(width+height), eye.GetY() + (direction.GetY() - plane.GetY())*(width+height));
	Vector p3(eye.GetX() + (direction.GetX() + plane.GetX())*(width+height), eye.GetY() + (direction.GetY() + plane.GetY())*(width+height));

	fov.AddPoint(p1);
	fov.AddPoint(p2);
	fov.AddPoint(p3);

	fov.Clip( Rect(Vector(1, 1), Vector(width-1, height-1)) );
}

void Minimap::SetMapPixels(const World& world, const Pixel& mapColor, const Pixel& wallColor)
{
	Pixel borderColor(255, 255, 255);

	for(int y = 0; y < height; y++)
	{
		for(int x = 0; x < width; x++)
		{
			Pixel p = mapColor;

			// Border pixel?
			if (y < BORDERHEIGHT || y >= height-BORDERHEIGHT || x < BORDERWIDTH || x >= width-BORDERWIDTH)
			{
				p = borderColor;
			}
			else
			{
				// Get coordinates without border
				int borderlessX = x - BORDERWIDTH;
				int borderlessY = y - BORDERHEIGHT;
				// Convert minimap coordinates to world coordinates, then to map coordinates
				int mapX = (leftEdge+(rightEdge-leftEdge)*borderlessX/(borderlessWidth)).GetInt();
				int mapY = (topEdge+(bottomEdge-topEdge)*borderlessY/(borderlessHeight)).GetInt();

				// Change to wall color if necessary
				if (!world.IsEmptyCell(mapX, mapY))
				{
					p = wallColor;
				}
			}
			
			SetPixel(x, y, p);
		}
	}
}

void Minimap::SetMapRange(const Vector& eyepoint, const World& world)
{
	// Calculate ranges in the world for the minimap view
	leftEdge = eyepoint.GetX() - WORLDSHOWSIZE;
	rightEdge = eyepoint.GetX() + WORLDSHOWSIZE;
	topEdge = eyepoint.GetY() - WORLDSHOWSIZE;
	bottomEdge = eyepoint.GetY() + WORLDSHOWSIZE;

	int worldWidth = world.GetWidth();
	int worldHeight = world.GetHeight();


	// Adjust size if outside of world
	if (leftEdge < 0)
	{
		rightEdge -= leftEdge;
		if (rightEdge > worldWidth)
			rightEdge = worldWidth;
		leftEdge = 0;
	}
	if (rightEdge > worldWidth)
	{
		leftEdge -= rightEdge-worldWidth;
		if (leftEdge < 0)
			leftEdge = 0;
		rightEdge = worldWidth;
	}

	if (topEdge < 0)
	{
		bottomEdge -= topEdge;
		if (bottomEdge > worldHeight)
			bottomEdge = worldHeight;
		topEdge = 0;
	}
	if (bottomEdge > worldHeight)
	{
		topEdge -= bottomEdge-worldHeight;
		if (topEdge < 0)
			topEdge = 0;
		bottomEdge = worldHeight;
	}	
}

const Poly& Minimap::GetViewPoly() const
{
	return fov;
}

